package com.boarsoft.boar.batch.biz;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.boarsoft.bean.ReplyInfo;
import com.boarsoft.boar.batch.Const.BatchSts;
import com.boarsoft.boar.batch.entity.BatchExecution;
import com.boarsoft.boar.batch.entity.BatchReport;
import com.boarsoft.common.Util;
import com.boarsoft.common.util.DateUtil;
import com.boarsoft.common.util.RandomUtil;
import com.boarsoft.hibernate.biz.SimpleBizImpl;
import org.springframework.beans.factory.annotation.Autowired;

import javax.transaction.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Description:
 * @Author: 刘超
 * @Date: 2023-06-29 15:52
 */
public class BatchReportBizImpl extends SimpleBizImpl implements BatchReportBiz {

	@Autowired
	private BatchExecutionBiz batchExecutionBiz;

	/**
	 * 增量进度上报
	 */
	private static final String REPORT_INCRE = "INCRE";
	/**
	 * 绝对进度上报
	 */
	private static final String REPORT_ABS = "ABS";

	private static final String FINISHED_PROGRESS = "100";

	@Override
	@Transactional
	public String save(BatchReport executionReport) {
		if (Util.strIsEmpty(executionReport.getId())) {
			this.getDao().save(executionReport);
		} else {
			this.getDao().update(executionReport);
		}
		return executionReport.getId();
	}

	@Override
	@Transactional
	public String saveReportAndExecution(String batchId, String progress, String phrase, String addr, String index) {

		BatchExecution batchExecution = batchExecutionBiz.getBybatchId(batchId);

		// 登记上报监息
		BatchReport report = new BatchReport();
		report.setBatchId(batchId);
		report.setProgress("1/" + batchExecution.getChildren());
		report.setPhrase(phrase);
		report.setAddr(addr);
		report.setIndex(index);
		report.setLastTime(DateUtil.yyyyMMddHHmmss());
		report.setNo(batchExecution.getNo());
		this.dao.save(report);

		//登记BatchExecution表子节点
		BatchExecution son = new BatchExecution();
		String id = RandomUtil.randomUUID();
		son.setId(id);
		son.setBatchId(id);
		son.setParentId(batchExecution.getId());
		son.setStartTime(new Date());
		son.setEndTime(new Date());
		son.setPath(batchExecution.getPath() + "/" + id);
		son.setStatus(BatchExecution.STATUS_SUCCESS);
		son.setProgress(FINISHED_PROGRESS);
		son.setAddr(addr);
		son.setPhrase(phrase);
		son.setLevel(son.getPath().contains("/") ? (short) son.getPath().split("/").length : (short) 1);
		this.dao.save(son);

		// execution表进度更新， 总进度 = 当前进度 + 单个子任务进度
		String updateSql = " update BatchExecution " +
				" set " +
				" dProgress = dProgress + ((1/iChildren) * tPhrasePer) " +
				" where vId = '" + batchExecution.getId() +
				"' and tNo = " + batchExecution.getNo();
		this.dao.executeUpdate(updateSql);

		/*
		// execution表阶段进度检查
		String roundSql = " update BatchExecution " +
				" set " +
				" dProgress = ROUND(dProgress, 0) " +
				" where vId = '" + batchExecution.getId() + "'" +
				" and tNo = " + batchExecution.getNo() +
				" and ( select count(1) from BatchReport " +
				" where 1=1 " +
				" and vProgress='1/" + batchExecution.getChildren() + "' " +
				" and vPhrase='" + phrase + "'" +
				" and vBatchId = '" + batchId + "'" +
				" and tNo = " + batchExecution.getNo() + " ) >= " + batchExecution.getChildren();
		this.dao.executeUpdate(roundSql);*/
		return "";
	}

	@Override
	@Transactional
	public String registSubProgress(String batchId, int dealCount, String phrase, String addr, String index) {
		BatchExecution batchExecution = batchExecutionBiz.getBybatchId(batchId);
		// 登记上报监息
		BatchReport report = new BatchReport();
		report.setBatchId(batchId);
		//此处登记当前子任务处理的数据量
		report.setProgress(String.valueOf(dealCount));
		report.setPhrase(phrase);
		report.setAddr(addr);
		report.setIndex(index);
		report.setLastTime(DateUtil.yyyyMMddHHmmss());
		report.setNo(batchExecution.getNo());
		this.dao.save(report);

		//登记BatchExecution表子节点
		BatchExecution son = new BatchExecution();
		String id = RandomUtil.randomUUID();
		son.setId(id);
		son.setBatchId(id);
		son.setParentId(batchExecution.getId());
		son.setStartTime(new Date());
		son.setEndTime(new Date());
		son.setPath(batchExecution.getPath() + "/" + id);
		son.setStatus(BatchExecution.STATUS_SUCCESS);
		son.setProgress(FINISHED_PROGRESS);
		son.setAddr(addr);
		son.setPhrase(phrase);
		son.setLevel(son.getPath().contains("/") ? (short) son.getPath().split("/").length : (short) 1);
		this.dao.save(son);

		// execution表进度更新， 总进度 = 当前进度 + 单个子任务进度
		String updateSql = " update BatchExecution " +
				" set " +
				" dProgress = dProgress + ((CAST(" + dealCount + " AS decimal(18,15))/CAST(iChildDataVol AS decimal(18,15))) * tPhrasePer) " +
				" where vId = '" + batchExecution.getId() +
				"' and tNo = " + batchExecution.getNo();
		this.dao.executeUpdate(updateSql);
		return "";
	}

	/**
	 *  处理直接上报
	 * @param batchId
	 * @param progress
	 * @param phrase
	 * @param addr
	 * @return
	 */
	@Override
	@Transactional
	public List<BatchExecution> saveAndUpdate(String batchId, String progress, String phrase, String addr) {
		BatchExecution batchExecution = batchExecutionBiz.getBybatchId(batchId);
		// 写登记表
		BatchReport report = new BatchReport();
		report.setBatchId(batchId);
		report.setProgress(progress);
		report.setPhrase(phrase);
		report.setAddr(addr);
		report.setLastTime(DateUtil.yyyyMMddHHmmss());
		report.setNo(batchExecution.getNo());
		this.dao.save(report);

		// 增量上报
		if (REPORT_INCRE.equals(judgeReportType(progress))) {
			String updateProgressSql = " update BatchExecution " +
					" set " +
					" dProgress = dProgress + " + Integer.parseInt(getProcessValue(progress)) +
					" where vId = '" + batchExecution.getId() + "'";
			int updateLine = this.dao.executeUpdate(updateProgressSql);
			log.info("batchId {}增量上报，进度为：{}，更新行数：{}", batchId, progress, updateLine);
		}
		// 绝对进度上报
		else if (REPORT_ABS.equals(judgeReportType(progress))) {
			String updateProgressSql = " update BatchExecution " +
					" set " +
					" dProgress = " + Integer.parseInt(getProcessValue(progress)) +
					" where vId = '" + batchExecution.getId() + "'";
			int updateLine = this.dao.executeUpdate(updateProgressSql);
			log.info("batchId {}绝对上报，进度为：{}, 更新行数：{}", batchId, progress, updateLine);
		}

		//当前状态及父状态处理
		List<BatchExecution> succeedExecution = statusCheck(batchExecution.getId());
		return succeedExecution;
	}

	@Override
	@Transactional
	public List<BatchExecution> saveAndUpdate(String batchId, String progress, String phrase, String addr, Object errMsg) {
		BatchExecution batchExecution = batchExecutionBiz.getBybatchId(batchId);
		List<BatchExecution> succeedExecution = saveAndUpdate(batchId, progress, phrase, addr);
		//更新状态为异常
		if(errMsg != null) {
			String updateSql = " update BatchExecution " +
					" set " +
					" tStatus = " + BatchExecution.STATUS_FAILED + "," +
					" txError = '" + JSON.toJSONString(errMsg) + "' " +
					" where vId = '" + batchExecution.getId() + "'";
			int updateLine = this.dao.executeUpdate(updateSql);
			log.info("更新 batchId {}，进度为：{}, 的执行记录状态为异常！。更新行数：{}", batchId, progress, updateLine);
		}
		return succeedExecution;
	}

	/**
	 * 当前状态及父状态处理
	 * @param id 数据技术主键
	 */
	@Transactional
	public List<BatchExecution> statusCheck(String id){
		List<BatchExecution> completeExecution = new ArrayList<>();

		//当前状态状态处理
		String pgs = (String)this.dao.getValue(" select progress from BatchExecution where vId = '" + id + "'");
		log.info("pgs={}", pgs);
		BigDecimal b = new BigDecimal(pgs);
		b = b.setScale(2, BigDecimal.ROUND_HALF_UP);
		if (b.compareTo(new BigDecimal(FINISHED_PROGRESS)) >= 0) {
			log.info("id:{}的进度已达到100，开始更新结束时间及记录状态.", id);
			String updateSql = " update BatchExecution " +
					" set " +
					" tStatus = 3 ," +
					" cEndTime = '" + Util.date2str(new Date(), Util.STDDTMF) + "'," +
					" dProgress = '100'"  +
					" where vId = '" + id + "'";
			int updateLine = this.dao.executeUpdate(updateSql);
			BatchExecution batchExecutionNew = batchExecutionBiz.get(id);
			completeExecution.add(batchExecutionNew);
//			completeExecution = this.updateParentStsRecursion(batchExecutionNew);
		}
		return completeExecution;
	}

	/**
	 * 判断上报为增量上报还是绝对上报
	 *
	 * @param progress 上报进度，如 +5、 5、 |5|
	 * @return
	 */
	private String judgeReportType(String progress) {
		if (progress.startsWith("|") && progress.endsWith("|")) {
			return REPORT_ABS;
		} else {
			return REPORT_INCRE;
		}
	}

	/**
	 * 获取绝对进度值或增量进度值
	 *
	 * @param progress
	 * @return
	 */
	private String getProcessValue(String progress) {
		if (progress.startsWith("|") && progress.endsWith("|")) {
			progress = progress.replace("|", "").trim();
		} else if (progress.startsWith("+")) {
			progress = progress.replace("+", "").trim();
		}
		return progress;
	}

	/**
	 * 执行子任务前概要上报
	 * @param batchId
	 * @param phrase
	 * @param addr
	 * @param childern
	 * @param childDataVol
	 * @param percent
	 * @return
	 */
	@Override
	public String saveAndUpdatereport(String batchId, String phrase, String addr, int childern, int childDataVol, int percent) {
		BatchExecution batchExecution = batchExecutionBiz.getBybatchId(batchId);
//		log.info("batchExecution: {}", JSON.toJSONString(batchExecution));

		BatchReport report = new BatchReport();
		report.setBatchId(batchId);
		report.setProgress("1/" + childern);
		report.setPhrase(phrase);
		report.setAddr(addr);
		report.setLastTime(DateUtil.yyyyMMddHHmmss());
		report.setNo(batchExecution.getNo());

		BigDecimal avgPagePercent = BigDecimal.ZERO;
		BigDecimal mistake = BigDecimal.ZERO;
		//处理分母为零
		if(childern == 0){
			avgPagePercent = BigDecimal.ZERO;
			mistake = BigDecimal.ZERO;
		} else {
			avgPagePercent = new BigDecimal(String.valueOf(percent))
					.divide(new BigDecimal(String.valueOf(childern)), 15, RoundingMode.HALF_UP);
			mistake = calcMistake(new BigDecimal(String.valueOf(percent)), avgPagePercent, childern);
		}

		JSONObject jsonObject = new JSONObject();
		jsonObject.put("AVG", avgPagePercent.toPlainString());
		jsonObject.put("MIS", mistake.toPlainString());
		report.setIndex(jsonObject.toJSONString());
		this.dao.save(report);

		batchExecution.setChildren(childern);
		batchExecution.setChildDataVol(childDataVol);
		batchExecution.setPhrasePer((short) percent);
		//如果分母为零时
		if(childern == 0){
			BigDecimal lastProgress = new BigDecimal(batchExecution.getProgress())
					.add(new BigDecimal(String.valueOf(percent)));
			batchExecution.setProgress(lastProgress.toPlainString());
		}

//		statusCheck(batchExecution.getId());
		return "";
	}

	/**
	 * 计算误差
	 *
	 * @param periodPercent  阶段总占比
	 * @param avgPagePercent 每个子任务平均占比
	 * @param totalPageSize  子任务数
	 * @return
	 */
	public static BigDecimal calcMistake(BigDecimal periodPercent, BigDecimal avgPagePercent, int totalPageSize) {
		BigDecimal mistake = BigDecimal.ZERO;
		BigDecimal temp = avgPagePercent.multiply(new BigDecimal(String.valueOf(totalPageSize)));
		if (new BigDecimal(String.valueOf(periodPercent)).compareTo(temp) != 0) {
			mistake = periodPercent.subtract(temp);
		}
		return mistake;
	}

	/**
	 * 逐级判断并更新父级进度及状态。
	 *
	 * @param be 默认入参为完成状态时调用，
	 */
	@Override
	@Transactional
	public List<BatchExecution> updateParentStsRecursion(BatchExecution be) {
		log.info("begin exe recursion status check. batchId:{}", be.getBatchId());

		//记录已经完成的执行记录，用于在service层发送MQ通知
		List<BatchExecution> complete = new ArrayList<BatchExecution>();
		complete.add(be);

		//如果记录的parentId为空，则表示入参记录没有父级，直接退出
		if (Util.strIsEmpty(be.getParentId())) {
			log.info("id:{}, batchId:{} Execution without parent, recursion return!", be.getId(), be.getBatchId());
			return complete;
		}

		//0、判断当前任务的父节点的子任务数是否等于上送记录数
		BatchExecution p = batchExecutionBiz.get(be.getParentId());
		//父节点的子任务数
		int children = p.getChildren();
		//上送记录数
		int childrenCount = dao.getTotal("select count(id) ".concat(" from ")
				.concat(BatchExecution.class.getName()).concat(" where 1=1 ")
				.concat(" and vParentId = '" + be.getParentId() + "' "));
		if(children > childrenCount){
			log.info("id:{}, batchId:{} Execution children is:{}, query children num is:{} " +
							"[ Execution children > query children num]  recursion return!",
									be.getId(), be.getBatchId(), children, childrenCount);
			return complete;
		}
		//1、查询兄弟节点是否全部完成。
		int unSuccessCount = dao.getTotal("select count(id) ".concat(" from ")
				.concat(BatchExecution.class.getName()).concat(" where 1=1 ")
				.concat(" and vParentId = '" + be.getParentId() + "' ")
				.concat(" and tStatus <> 3 "));
		//2、存在未完成的兄弟节点则退出
		if (unSuccessCount >= 1) {
			log.info("id:{}, batchId:{} Execution brother node without succeed!, recursion return! unSuccessCount:{}",
					be.getId(), be.getBatchId(), unSuccessCount);
			return complete;
		//3、兄弟节点全部完成
		} else {
			//4、处理父节点并将状态更新为完成
			String updateSql = " update BatchExecution " +
					" set " +
					" tStatus = 3 ," +
					" cEndTime = '" + Util.date2str(new Date(), Util.STDDTMF) + "'," +
					" dProgress = '100'"  +
					" where vId = '" + be.getParentId() + "'";
			int updateLine = this.dao.executeUpdate(updateSql);
			BatchExecution parent = batchExecutionBiz.get(be.getParentId());
			complete.add(parent);
			//5、递归向上查找，直到尽头
			if (Util.strIsEmpty(parent.getParentId())) {
				return complete;
			}
			BatchExecution superParent = batchExecutionBiz.get(parent.getParentId());
			if (superParent != null && Util.strIsNotEmpty(superParent.getId())) {
				complete.addAll(updateParentStsRecursion(parent));
			}
		}
		// distinct
		List<BatchExecution> uniq = new ArrayList<>(complete.stream().collect(Collectors.toMap(BatchExecution::getId, Function.identity(), (a, b) -> a)).values());
		return uniq;
	}

	@Override
	public String report1(String[] args) {
		return null;
	}

	@Override
	public String report2(String[] args) {
		return null;
	}

	@Override
	public String report3(String[] args) {
		return null;
	}

	@Override
	public String report4(String[] args) {
		return null;
	}
}
